package xyz.jcat.common.util;

import java.io.File;
import java.io.FileFilter;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ReflectUtils extends org.springframework.util.ReflectionUtils {

    private static final Map<String, Optional<Field>> fieldCache = new HashMap<>();

    /**
     * 获取字段对象
     *
     * @param clazz
     * @param fieldName
     * @return
     */
    public static Field getField(Class clazz, String fieldName) {
        String key = clazz.getName() + fieldName;
        Optional<Field> fieldOpt = fieldCache.get(key);
        if (fieldOpt == null) {
            Field field = findField(clazz, fieldName);
            fieldOpt = Optional.ofNullable(field);
            fieldCache.put(key, fieldOpt);
        }
        if (fieldOpt.isPresent()) {
            return fieldOpt.get();
        }
        return null;
    }

    /**
     * 获得包下面的所有的class
     *
     * @param
     * @return List包含所有class的实例
     */

    public static List<Class<?>> getPackageClasses(String packagePath) {
        List<Class<?>> clazzs = new ArrayList<>();
        // 是否循环搜索子包
        boolean recursive = true;
        // 包名对应的路径名称
        String packageDirName = packagePath.replace('.', '/');
        Enumeration<URL> dirs;

        try {
            dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
            while (dirs.hasMoreElements()) {

                URL url = dirs.nextElement();
                String protocol = url.getProtocol();

                if ("file".equals(protocol)) {
                    String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
                    findClassInPackageByFile(packagePath, filePath, recursive, clazzs);
                } else if ("jar".equals(protocol)) {
                    findClassInPackageByJar(url.getPath(), recursive, clazzs);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return clazzs;
    }

    private static void findClassInPackageByJar(String jarPath, boolean recursive, List<Class<?>> clazzs) {
        String[] jarInfo = jarPath.split("!");
        String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));
        String packagePath = jarInfo[1].substring(1);
        try {
            JarFile jarFile = new JarFile(jarFilePath);
            Enumeration<JarEntry> entrys = jarFile.entries();
            while (entrys.hasMoreElements()) {
                JarEntry jarEntry = entrys.nextElement();
                String entryName = jarEntry.getName();
                if (entryName.endsWith(".class")) {
                    if (recursive) {
                        if (entryName.startsWith(packagePath)) {
                            clazzs.add(getClass(entryName, packagePath));
                        }
                    } else {
                        int index = entryName.lastIndexOf("/");
                        String myPackagePath;
                        if (index != -1) {
                            myPackagePath = entryName.substring(0, index);
                        } else {
                            myPackagePath = entryName;
                        }
                        if (myPackagePath.equals(packagePath)) {
                            clazzs.add(getClass(entryName, packagePath));
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static Class getClass(String entryName, String packagePath) throws ClassNotFoundException {
        entryName = entryName.substring(packagePath.length());
        String className = entryName.replace("/", ".").substring(1, entryName.lastIndexOf("."));
        return Thread.currentThread().getContextClassLoader().loadClass(className);
    }

    /**
     * 在package对应的路径下找到所有的class
     */
    public static void findClassInPackageByFile(String packageName, String filePath, final boolean recursive,
                                                List<Class<?>> clazzs) {
        File dir = new File(filePath);
        if (!dir.exists() || !dir.isDirectory()) {
            return;
        }
        // 在给定的目录下找到所有的文件，并且进行条件过滤
        File[] dirFiles = dir.listFiles(new FileFilter() {

            public boolean accept(File file) {
                boolean acceptDir = recursive && file.isDirectory();// 接受dir目录
                boolean acceptClass = file.getName().endsWith("class");// 接受class文件
                return acceptDir || acceptClass;
            }
        });

        for (File file : dirFiles) {
            if (file.isDirectory()) {
                findClassInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, clazzs);
            } else {
                String className = file.getName().substring(0, file.getName().length() - 6);
                try {
                    clazzs.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
